package com.egglazy.Utils;

import io.netty.channel.ChannelOption;
import io.netty.handler.timeout.ReadTimeoutHandler;
import io.netty.handler.timeout.WriteTimeoutHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatusCode;
import org.springframework.http.client.reactive.ReactorClientHttpConnector;
import org.springframework.web.reactive.function.client.ExchangeStrategies;
import org.springframework.web.reactive.function.client.WebClient;
import org.springframework.web.reactive.function.client.WebClientResponseException;
import org.springframework.web.util.UriComponentsBuilder;
import reactor.core.publisher.Mono;
import reactor.netty.http.client.HttpClient;

import java.net.ConnectException;
import java.time.Duration;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;

public class WebClientUtil {

    private static final Logger logger = LoggerFactory.getLogger(WebClientUtil.class);
    private static final Duration DEFAULT_TIMEOUT = Duration.ofSeconds(30);

    private static final WebClient webClient;
    private static final int TIMEOUT_MILLIS = 5000; // 连接超时时间
    private static final int READ_TIMEOUT_SECONDS = 10; // 读取超时时间
    private static final int WRITE_TIMEOUT_SECONDS = 10; // 写入超时时间
    private static final int MAX_IN_MEMORY_SIZE = 16 * 1024 * 1024; // 响应体大小限制，默认为16MB

    static {
        // 配置HTTP客户端
        HttpClient httpClient = HttpClient.create()
                .option(ChannelOption.CONNECT_TIMEOUT_MILLIS, TIMEOUT_MILLIS)
                .responseTimeout(Duration.ofMillis(TIMEOUT_MILLIS))
                .doOnConnected(conn -> conn
                        .addHandlerLast(new ReadTimeoutHandler(READ_TIMEOUT_SECONDS, TimeUnit.SECONDS))
                        .addHandlerLast(new WriteTimeoutHandler(WRITE_TIMEOUT_SECONDS, TimeUnit.SECONDS)));
        // 配置响应体大小限制
        ExchangeStrategies exchangeStrategies = ExchangeStrategies.builder()
                .codecs(configurer -> configurer.defaultCodecs().maxInMemorySize(MAX_IN_MEMORY_SIZE))
                .build();
        webClient = WebClient.builder()
                .clientConnector(new ReactorClientHttpConnector(httpClient))
                .exchangeStrategies(exchangeStrategies)
                .build();
    }


    /**
     * 发送GET请求
     *
     * @param url 请求URL
     * @param responseType 响应类型
     * @param <T> 响应类型泛型
     * @return 响应结果
     */
    public static <T> Mono<T> get(String url, Class<T> responseType) {
        return getO(url, null, responseType);
    }

    /**
     * 发送带自定义请求头的GET请求
     *
     * @param url 请求URL
     * @param headers 自定义请求头
     * @param responseType 响应类型
     * @param <T> 响应类型泛型
     * @return 响应结果
     */
    public static <T> Mono<T> getO(String url, Map<String, String> headers, Class<T> responseType) {
        return webClient.get()
                .uri(url)
                .headers(httpHeaders -> setHeaders(httpHeaders, headers))
                .retrieve()
                .bodyToMono(responseType)
                .timeout(DEFAULT_TIMEOUT)
                .onErrorResume(WebClientUtil::handleError);
    }

    /**
     * 发送带参数的GET请求
     * @param url 请求URL
     * @param params 请求参数
     * @param responseType 响应类型
     * @param <T> 响应类型泛型
     * @return 响应结果
     */
    public static <T> Mono<T> get(String url, Map<String, String> params, Class<T> responseType) {
        return get(url, params, null, responseType);
    }

    /**
     * 发送带参数和自定义请求头的GET请求
     *
     * @param url 请求URL
     * @param params 请求参数
     * @param headers 自定义请求头
     * @param responseType 响应类型
     * @param <T> 响应类型泛型
     * @return 响应结果
     */
    public static <T> Mono<T> get(String url, Map<String, String> params, Map<String, String> headers, Class<T> responseType) {
        // 构建带查询参数的 URL
        UriComponentsBuilder uriBuilder = UriComponentsBuilder.fromUriString(url);
        if (params != null) {
            params.forEach(uriBuilder::queryParam);
        }
        return webClient.get()
                .uri(uriBuilder.build().toUri())
                .headers(httpHeaders -> setHeaders(httpHeaders, headers))
                .retrieve()
                .bodyToMono(responseType)
                .timeout(DEFAULT_TIMEOUT)
                .onErrorResume(WebClientUtil::handleError);
    }

    /**
     * 发送POST请求
     *
     * @param url 请求URL
     * @param body 请求体
     * @param responseType 响应类型
     * @param <T> 响应类型泛型
     * @return 响应结果
     */
    public static <T> Mono<T> post(String url, Object body, Class<T> responseType) {
        return post(url, body, null, responseType);
    }

    /**
     * 发送带自定义请求头的POST请求
     *
     * @param url 请求URL
     * @param body 请求体
     * @param headers 自定义请求头
     * @param responseType 响应类型
     * @param <T> 响应类型泛型
     * @return 响应结果
     */
    public static <T> Mono<T> post(String url, Object body, Map<String, String> headers, Class<T> responseType) {
        return webClient.post()
                .uri(url)
                .headers(httpHeaders -> setHeaders(httpHeaders, headers))
                .bodyValue(body)
                .retrieve()
                .bodyToMono(responseType)
                .timeout(DEFAULT_TIMEOUT)
                .onErrorResume(WebClientUtil::handleError);
    }


    /**
     * 异步发送GET请求
     *
     * @param url 请求URL
     * @param responseType 响应类型
     * @param <T> 响应类型泛型
     * @return CompletableFuture包装的响应结果
     */
    public static <T> CompletableFuture<T> getAsync(String url, Class<T> responseType) {
        return get(url, responseType).toFuture();
    }

    /**
     * 异步发送带自定义请求头的GET请求
     *
     * @param url 请求URL
     * @param headers 自定义请求头
     * @param responseType 响应类型
     * @param <T> 响应类型泛型
     * @return CompletableFuture包装的响应结果
     */
    public static <T> CompletableFuture<T> getAsyncO(String url, Map<String, String> headers, Class<T> responseType) {
        return getO(url, headers, responseType).toFuture();
    }

    /**
     * 异步发送带参数的GET请求
     *
     * @param url 请求URL
     * @param params 请求参数
     * @param responseType 响应类型
     * @param <T> 响应类型泛型
     * @return CompletableFuture包装的响应结果
     */
    public static <T> CompletableFuture<T> getAsync(String url, Map<String, String> params, Class<T> responseType) {
        return get(url, params, responseType).toFuture();
    }

    /**
     * 异步发送带参数和自定义请求头的GET请求
     *
     * @param url 请求URL
     * @param params 请求参数
     * @param headers 自定义请求头
     * @param responseType 响应类型
     * @param <T> 响应类型泛型
     * @return CompletableFuture包装的响应结果
     */
    public static <T> CompletableFuture<T> getAsync(String url, Map<String, String> params, Map<String, String> headers, Class<T> responseType) {
        return get(url, params, headers, responseType).toFuture();
    }

    /**
     * 异步发送POST请求
     *
     * @param url 请求URL
     * @param body 请求体
     * @param responseType 响应类型
     * @param <T> 响应类型泛型
     * @return CompletableFuture包装的响应结果
     */
    public static <T> CompletableFuture<T> postAsync(String url, Object body, Class<T> responseType) {
        return post(url, body, responseType).toFuture();
    }

    /**
     * 异步发送带自定义请求头的POST请求
     *
     * @param url 请求URL
     * @param body 请求体
     * @param headers 自定义请求头
     * @param responseType 响应类型
     * @param <T> 响应类型泛型
     * @return CompletableFuture包装的响应结果
     */
    public static <T> CompletableFuture<T> postAsync(String url, Object body, Map<String, String> headers, Class<T> responseType) {
        return post(url, body, headers, responseType).toFuture();
    }


    /**
     * 设置请求头
     *
     * @param httpHeaders HttpHeaders对象
     * @param headers 自定义请求头
     */
    private static void setHeaders(HttpHeaders httpHeaders, Map<String, String> headers) {
        if (headers != null && !headers.isEmpty()) {
            headers.forEach(httpHeaders::add);
        }
    }

    /**
     * 处理请求异常
     *
     * @param throwable 异常
     * @param <T> 响应类型泛型
     * @return 包含错误信息的Mono
     */
    private static <T> Mono<T> handleError(Throwable throwable) {
        logger.error("WebClient请求异常: {}", throwable.getMessage());

        switch (throwable) {
            case WebClientResponseException ex -> {
                HttpStatusCode status = ex.getStatusCode();
                String body = ex.getResponseBodyAsString();
                logger.error("HTTP错误: 状态码={}, 响应体={}", status.value(), body);
                return Mono.error(new RuntimeException("HTTP请求失败: " + status.value() + " - " + body));
            }
            case ConnectException connectException -> {
                return Mono.error(new RuntimeException("连接服务器失败: " + connectException.getMessage()));
            }
            case TimeoutException timeoutException -> {
                return Mono.error(new RuntimeException("请求超时: " + timeoutException.getMessage()));
            }
            default -> {
            }
        }
        return Mono.error(new RuntimeException("请求失败: " + throwable.getMessage()));
    }
}